<script>
     /*
        三、签名 DEMO

                  1、DEMO示例

                             为方便开发者快速接入，我们提供了Java 和 Go 语言对应的签名 DEMO示例供参考。

                                ①、open-pay-sinature-java-demo  适用于Java开发者

                                ②、open-pay-sinature-go-demo    适用于GO开发者


                   2、回调签名算法

                             支付回调通知开发者服务端时，会使用如下的算法进行签名，供开发者验证请求的来源。


                             <1>、在 [抖音开放平台--小程序--能力---支付--支付产品管理---支付设置] 中获取 [Token(令牌)]，按照接口文档将所有请求字段 
                                  (验证时注意不包含签名本身，不包含字段与回调类型标记 type 常量字段) 内容与平台上配置的token一起，进行字典排序。

                                    图示：

                                        https://p9-douyin-open.byteimg.com/tos-cn-i-9nona0m7ha/3f971b11fcd6460b9c1a1480603b3417~tplv-9nona0m7ha-image.image

                             <2>、所有字段内容连接成一个字符串

                             <3>、使用 SHA-1 算法计算字符串摘要作为签名

                             上述步骤计算出的签名 signature ,和支付回调请求体力的signature对比，如果不一致，说明请求不可信任 (如被篡改)。


                    3、请求示例

                            以 "支付结果回调接口" 为例，小程序的 token值为　token, 请求回调参数如下：

                                        {
                                            "timestamp": "timestamp",
                                            "nonce": "nonce",
                                            "msg": "msg",
                                            "msg_signature": "msg_signature",
                                            "type": "payment"
                                        }

                               ★、node.js 示例：

                                        // http handler GET 
                                        function handler(ctx) {
                                            const {timestamp, msg, nonce} = ctx.body;
                                            const strArr = [token, timestamp, nonce, msg].sort();
                                            const str = strArr.join('');
                                            const _signature = require('crypto').createHash('sha1').update(str).digest('hex');
                                        }

                    4、回调响应 

                              在开发者服务端收到回调且处理成功后，需要按以下json 返回表示处理成功，否则会认为通知失败进行重试。

                                        {
                                            "err_no": 0,
                                            "err_tips": "success"
                                        }

                    5、请求签名算法

                              发往小程序服务端的请求，在没有特殊说明时，均需要使用担保支付密钥进行签名，用于保证请求的来源：

                                    ①、sing、app_id、thirdparty_id、prod_id 字段用于标识身份字段，不参与签名。将其他请求参数的字段内容 (不包含氢气参数的key)
                                     
                                          与支付 salt一起进行字段排序后，使用 "&" 符号链接。

                                    ②、还用MD5算法对该字符串计算摘要，作为结果。

                                    ③、部分请求字段非必填或条件选填，在值为空的情况下不需要参与签名；参与加签的字段均以 POST 请求中的 body 内容为准，不考虑参数默认值等规则。
                                            
                                            对于对象类型与数组类型的参数，使用POST中的字符串原串进行左右去除空格后进行加签。
                                        
                                    ④、如有其他安全性需要，可以在请求中添加 nonce 字段，该字段无任何业务影响，仅影响加签内容，使统一请求的多次签名不同。

                                    ⑤、推荐使用 "签名校验工具"  （https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/developer-instrument/development-assistance/sign/）
                                           辅助验证生产签名的准确性。


                                    ★、Java 示例加签：

                                                        public static String Map2String(Map<String, Object> paramsMap) {
                                                            StringBuilder stringBuilder = new StringBuilder();
                                                            stringBuilder.append("map[");
                                                            List<String> params = new ArrayList<>();
                                                            List<String> paramsArr = new ArrayList<>();
                                                            for (String key : paramsMap.keySet()) {
                                                                paramsArr.add(key);
                                                            }
                                                            Collections.sort(paramsArr);
                                                            for (String key : paramsArr) {
                                                                Object objValue = paramsMap.get(key);
                                                                if (objValue instanceof Map) {
                                                                    params.add(key + ":" + Map2String((Map) objValue));
                                                                } else if (objValue instanceof List) {
                                                                    params.add(key + ":" + List2String((List) objValue));
                                                                } else {
                                                                    params.add(key + ":" + objValue.toString());
                                                                }
                                                            }
                                                            stringBuilder.append(String.join(" ", params));
                                                            stringBuilder.append("]");
                                                            return stringBuilder.toString();
                                                        }

                                                        public static String List2String(List<Object> list) {
                                                            StringBuilder stringBuilder = new StringBuilder();
                                                            stringBuilder.append("[");
                                                            List<String> paramsArr = new ArrayList<>();
                                                            for (Object item : list) {
                                                                if (item instanceof List) {
                                                                    paramsArr.add(List2String((List) item));
                                                                } else if (item instanceof Map) {
                                                                    paramsArr.add(Map2String((Map) item));
                                                                } else {
                                                                    paramsArr.add(item.toString());
                                                                }
                                                            }
                                                            stringBuilder.append(String.join(" ", paramsArr));
                                                            stringBuilder.append("]");
                                                            return stringBuilder.toString();
                                                        }

                                                        public final static List<String> REQUEST_NOT_NEED_SIGN_PARAMS = Arrays.asList("app_id", "thirdparty_id", "sign", "other_settle_params");

                                                        public static String requestSign(Map<String, Object> paramsMap) {
                                                            List<String> paramsArr = new ArrayList<>();
                                                            for (Map.Entry<String, Object> entry : paramsMap.entrySet()) {
                                                                String key = entry.getKey();
                                                                if (REQUEST_NOT_NEED_SIGN_PARAMS.contains(key)) {
                                                                    continue;
                                                                }
                                                                Object objValue = entry.getValue();
                                                                String value = "";
                                                                if (objValue instanceof Map) {
                                                                    value = Map2String((Map) objValue);
                                                                } else if (objValue instanceof List) {
                                                                    value = List2String((List) objValue);
                                                                } else {
                                                                    value = entry.getValue().toString();
                                                                }

                                                                value = value.trim();
                                                                if (value.startsWith("\"") && value.endsWith("\"") && value.length() > 1) {
                                                                    value = value.substring(1, value.length() - 1);
                                                                }
                                                                value = value.trim();
                                                                if (value.equals("") || value.equals("null")) {
                                                                    continue;
                                                                }
                                                                paramsArr.add(value);
                                                            }
                                                            paramsArr.add(SALT);
                                                            Collections.sort(paramsArr);
                                                            StringBuilder signStr = new StringBuilder();
                                                            String sep = "";
                                                            for (String s : paramsArr) {
                                                                signStr.append(sep).append(s);
                                                                sep = "&";
                                                            }
                                                            return md5FromStr(signStr.toString());
                                                        }
                                                        private static String md5FromStr(String inStr) {
                                                            MessageDigest md5;
                                                            try {
                                                                md5 = MessageDigest.getInstance("MD5");
                                                            } catch (NoSuchAlgorithmException e) {
                                                                e.printStackTrace();
                                                                return "";
                                                            }

                                                            byte[] byteArray = inStr.getBytes(StandardCharsets.UTF_8);
                                                            byte[] md5Bytes = md5.digest(byteArray);
                                                            StringBuilder hexValue = new StringBuilder();
                                                            for (byte md5Byte : md5Bytes) {
                                                                int val = ((int) md5Byte) & 0xff;
                                                                if (val < 16) {
                                                                    hexValue.append("0");
                                                                }
                                                                hexValue.append(Integer.toHexString(val));
                                                            }
                                                            return hexValue.toString();
                                                        }
                                                                                            
                                       
    */
</script>